﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Collections;
using System.Reflection;

namespace CH.Study.ConsoleApp.ExpressionTree
{
    public class QueryEnumerator<T> : IEnumerator<T>, IEnumerator, IDisposable
    {
        DbDataReader reader;
        PropertyInfo[] fields;
        int[] fieldLookup;
        T current;


        public QueryEnumerator(DbDataReader reader)
        {
            this.reader = reader;
            this.fields = typeof(T).GetProperties();
        }


        public T Current
        {
            get { return this.current; }
        }


        object IEnumerator.Current
        {
            get { return this.current; }
        }


        public bool MoveNext()
        {
            if (this.reader.Read())
            {
                if (this.fieldLookup == null)
                {
                    this.InitFieldLookup();
                }
                Type t = typeof(T);
                T instance;
                if (!t.FullName.Contains("AnonymousType"))
                {

                    instance = (T)Activator.CreateInstance(typeof(T));
                    for (int i = 0, n = this.fields.Length; i < n; i++)
                    {
                        int index = this.fieldLookup[i];
                        if (index >= 0)
                        {
                            PropertyInfo fi = this.fields[i];
                            if (this.reader.IsDBNull(index))
                            {

                                fi.SetValue(instance, null, null);
                            }
                            else
                            {
                                fi.SetValue(instance, this.reader.GetValue(index), null);
                            }
                        }
                    }
                }
                else 
                {
                    object[] ol = new object[t.GetProperties().Length];
                    for (int i = 0, n = this.fields.Length; i < n; i++)
                    {
                        int index = this.fieldLookup[i];
                        if (index >= 0)
                        {
                            
                            if (this.reader.IsDBNull(index))
                            {

                                ol[i] = null;
                            }
                            else
                            {
                                ol[i]= this.reader.GetValue(index);
                            }
                        }
                    }

                    instance = (T)Activator.CreateInstance(typeof(T), ol);
                
                }
                this.current = instance;
                return true;
            }
            return false;
        }


        public void Reset()
        {
        }


        public void Dispose()
        {
            // 因为在QueryExpressionProvider Execute方法(SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))中
            //已经设定了参数CommandBehavior.CloseConnection
            // 所以当this.reader 关闭的时候与之关联的数据连接也会被关闭
            this.reader.Close();
            this.reader.Dispose();
        }


        private void InitFieldLookup()
        {
            Dictionary<string, int> map = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
            for (int i = 0, n = this.reader.FieldCount; i < n; i++)
            {
                map.Add(this.reader.GetName(i), i);
            }
            this.fieldLookup = new int[this.fields.Length];
            for (int i = 0, n = this.fields.Length; i < n; i++)
            {
                int index;
                if (map.TryGetValue(this.fields[i].Name, out index))
                {
                    this.fieldLookup[i] = index;
                }
                else
                {
                    this.fieldLookup[i] = -1;
                }
            }
        }
    }
}
